package com.autumn.evaluator.nodes;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import com.autumn.evaluator.Enums;
import com.autumn.evaluator.Function;
import com.autumn.evaluator.FunctionManager;
import com.autumn.evaluator.FunctionParam;
import com.autumn.evaluator.FunctionParams;
import com.autumn.evaluator.IParseContext;
import com.autumn.evaluator.Token;
import com.autumn.evaluator.Utils;
import com.autumn.evaluator.Variant;
import com.autumn.evaluator.exception.FunctionCallException;
import com.autumn.evaluator.exception.FunctionException;
import com.autumn.evaluator.exception.FunctionNotExistException;

/**
 * 函数
 */
public final class FunctionNode extends AbstractTreeNode implements Serializable {

	/**
	 * 序列号
	 */
	private static final long serialVersionUID = -4690221043889702990L;

	/**
	 * 实例化 FunctionNode 类新实例
	 *
	 * @param name
	 *            名称
	 * @param collection
	 *            集合
	 */
	public FunctionNode(String name, AbstractTreeNode[] collection) {
		super();
		this.setName(name);
		this.setArguments(this.readArguments(collection));
	}

	/**
	 * 实例化 FunctionNode 类新实例
	 *
	 * @param token
	 *            标记
	 * @param collection
	 *            集合
	 */
	public FunctionNode(Token token, AbstractTreeNode[] collection) {
		super(token);
		this.setName(token.getText().toUpperCase());
		this.setArguments(this.readArguments(collection));
	}

	/**
	 * 获取函数名称
	 */
	private String name;

	public String getName() {
		return name;
	}

	private void setName(String value) {
		name = value;
	}

	private List<AbstractTreeNode> readArguments(AbstractTreeNode[] collection) {
		ArrayList<AbstractTreeNode> items = new ArrayList<AbstractTreeNode>();
		for (AbstractTreeNode item : collection) {
			item.parent = this;
			items.add(item);
		}
		return items;
	}

	/**
	 * 获取参数集合
	 */
	private List<AbstractTreeNode> arguments;

	public List<AbstractTreeNode> getArguments() {
		return arguments;
	}

	private void setArguments(List<AbstractTreeNode> value) {
		arguments = value;
	}

	/**
	 * 获取节点类型
	 */
	@Override
	public Enums.NodeType getType() {
		return Enums.NodeType.CALL;
	}

	/**
	 * 克隆
	 *
	 * @return
	 */
	@Override
	public AbstractTreeNode dupNode() {
		FunctionNode fun;
		if (this.getIsNil()) {
			fun = new FunctionNode(this.getName(),
					this.getArguments().toArray(new AbstractTreeNode[this.getArguments().size()]));
		} else {
			fun = new FunctionNode(this.getToken(),
					this.getArguments().toArray(new AbstractTreeNode[this.getArguments().size()]));
		}
		return fun;
	}

	/**
	 * 获取首个标记
	 */
	@Override
	public Token getFirstToken() {
		return this.getToken();
	}

	private Token lastToken = null;
	private boolean readLastToken = false;

	/**
	 * 获取最后标记
	 */
	@Override
	public Token getLastToken() {
		if (!readLastToken) {
			if (this.getArguments().size() > 0) {
				this.lastToken = this.getArguments().get(getArguments().size() - 1).getLastToken();
			} else {
				this.lastToken = this.getToken();
			}
			readLastToken = true;
		}
		return this.lastToken;
	}

	/**
	 * @param desc
	 * @return
	 */
	private FunctionException throwFunctionException(String desc) {
		if (this.getIsNil()) {
			throw new FunctionException(-1, 0, this.getName(), desc);
		} else {
			throw new FunctionException(this.getToken().getStartIndex(), this.getToken().getLength(), this.getName(),
					desc);
		}
	}

	/**
	 * 节点表达式
	 *
	 * @param grammarType
	 *            语言类型
	 * @return
	 */
	@Override
	public String toString() {
		StringBuilder str = new StringBuilder();
		str.append(this.getName());
		str.append("(");
		for (int i = 0; i < this.getArguments().size(); i++) {
			if (i > 0) {
				str.append(",");
			}
			AbstractTreeNode value = this.getArguments().get(i);
			if (!(value instanceof NullNode)) {
				str.append(value.toString());
			}
		}
		str.append(")");
		return str.toString();
	}

	/**
	 * @param parseContext
	 * @return
	 */
	@Override
	public Variant parse(IParseContext parseContext) {
		Utils.checkNotNull("parseContext", parseContext);
		Function fun = FunctionManager.getFunction(this.getName());
		if (fun != null) {
			FunctionParams funParams = new FunctionParams(this.getName());
			for (int i = 0; i < this.getArguments().size(); i++) {
				int orderId = i + 1;
				// funParams.add(new FunctionParam(orderId, parseContext,
				// this.getArguments().get(i).parse, fun.getGetFunParams().getItem(orderId)));
				int finalI = i;
				funParams.add(new FunctionParam(orderId, parseContext,
						iParseContext -> this.getArguments().get(finalI).parse(iParseContext),
						fun.getGetFunParams().getItem(orderId)));
			}
			// 无参数函数验证
			if (!fun.getIsDynamicParam() && fun.getMinParamCount() <= 0 && fun.getGetFunParams().getCount() == 0
					&& funParams.getCount() > 0) {
				this.throwFunctionException(String.format("参数过多,最多只能输入 %s 个参数,实际传入 %s 个参数。",
						fun.getGetFunParams().getCount(), funParams.getCount()));
			}
			// 低于最小参数
			if (fun.getMinParamCount() > 0 && funParams.getCount() < fun.getMinParamCount()) {
				this.throwFunctionException(
						String.format("缺少参数,至少需要输入 %s 个参数,实际传入 %s 个参数。", fun.getMinParamCount(), funParams.getCount()));
			}
			if (fun.getGetFunParams().getCount() > 0) {
				// 非动态参数时，传入大于设置参数，则抛参数过多
				if (!fun.getIsDynamicParam() && funParams.getCount() > fun.getGetFunParams().getCount()) {
					this.throwFunctionException(String.format("参数过多,最多只能输入 %s 个参数,实际传入 %s 个参数。",
							fun.getGetFunParams().getCount(), funParams.getCount()));
				}
				if (fun.getGetFunParams().getCount() > funParams.getCount()) // 添加默认参数
				{
					for (Iterator<FunctionParam> it = fun.getGetFunParams().getEnumerator(); it.hasNext();) {
						FunctionParam p = it.next();
						if (!p.getDefaultValue().isNull()) {
							FunctionParam p1 = funParams.getItem(p.getOrderID());
							if (p1 == null) {
								funParams.add(new FunctionParam(p.getOrderID(), p.getName(), p.getCaption(),
										p.getDefaultValue().clone(), p.getDefaultValue().getVariantType()));
							}
						}
					}
				}
			}
			try {
				return fun.getCall().call(this.getName(), funParams, parseContext.getContext());
			} catch (RuntimeException err) {
				if (this.getIsNil()) {
					throw new FunctionCallException(-1, this.getName().length(), this.getName(), err.getMessage(), err);
				} else {
					throw new FunctionCallException(this.getToken().getStartIndex(), this.getToken().getLength(),
							this.getName(), err.getMessage(), err);
				}
			}
		} else {
			if (this.getIsNil()) {
				throw new FunctionNotExistException(-1, this.getName().length(), this.getName(),
						String.format("函数 %s 未注册。", this.getName()));
			} else {
				throw new FunctionNotExistException(this.getToken().getStartIndex(), this.getToken().getLength(),
						this.getName(), String.format("函数 %s 未注册。", this.getName()));
			}
		}
	}

}